# encoding: utf-8
"""
@author: youfeng
@file: proxy_requests.py
@time: 2018/9/1 上午10:22
"""
import random
import time

import requests
from fake_useragent import UserAgent

from proxy.proxy_type import ProxyType


class ProxyRequests(object):
    __USER_AGENT_LIST = [
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1"
        "Mozilla/5.0 (X11; CrOS i686 2268.111.0) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.57 "
        "Safari/536.11",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1092.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.6 (KHTML, like Gecko) Chrome/20.0.1090.0 Safari/536.6",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/19.77.34.5 Safari/537.1",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.9 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.0) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.36 Safari/536.5",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 Safari/536.3",
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_0) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1063.0 "
        "Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1062.0 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.1 Safari/536.3",
        "Mozilla/5.0 (Windows NT 6.2) AppleWebKit/536.3 (KHTML, like Gecko) Chrome/19.0.1061.0 Safari/536.3",
        "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24",
        "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/535.24 (KHTML, like Gecko) Chrome/19.0.1055.1 Safari/535.24"
    ]

    def __init__(self, proxy_handler, log):
        self.log = log
        self.__ua = UserAgent()
        self.__proxy_handler = proxy_handler
        self.__session = self.__get_session()
        # 加载一个默认代理
        self.switch_proxy()

    # 获得session
    def __get_session(self):
        session = requests.Session()
        session.headers['User-Agent'] = self.__get_user_agent()
        return session

    # 获得浏览器信息
    def __get_user_agent(self):
        if self.__ua is None:
            return random.choice(self.__USER_AGENT_LIST)

        return str(self.__ua.random)

    def set_header(self, headers):
        """
        切换请求头信息
        :param headers: 请求头部信息
        :return:
        """
        if not isinstance(headers, dict):
            raise Exception("headers 类型错误: 需要 dict类型 当前类型: type = {}".format(type(headers)))
        self.__session.headers = headers

    def switch_session(self, session=None):
        """
        切换 session 在session不可复用场景下使用
        :param session:
        :return:
        """
        if not isinstance(session, requests.Session):
            self.__session = requests.session()
        else:
            self.__session = session

    # 切换代理
    def switch_proxy(self, proxy_type=ProxyType.KUNPENG_STATIC, host=None):
        """
        切换代理
        :param proxy_type: 代理类型，请查看 ProxyType 类型
        :param host: 当前访问的域名，动态代理需要
        :return: 返回代理: 例如: {"http": "http://10.10.10.10:8888"}
        """
        self.__session.proxies = self.__proxy_handler.get_proxy(proxy_type, host)

    def __request(self, func, url, **kwargs):
        # 获得过期时间
        kwargs['timeout'] = kwargs.get('timeout', 10)

        # 获得正确状态码
        success_code = kwargs.pop("success_code", 200)
        if not isinstance(success_code, tuple) and \
                not isinstance(success_code, int) and \
                not isinstance(success_code, list):
            raise Exception(
                "success_code 类型错误: 必须为: tuple or list or int 类型， 当前类型 type = {}".format(type(success_code)))

        # 获得尝试次数限制
        try_times = kwargs.pop("try_times", 3)

        # 获得代理类型
        proxy_type = kwargs.pop("proxy_type", ProxyType.KUNPENG_STATIC)

        for _ in xrange(try_times):
            try:
                resp = func(url=url, **kwargs)
                if resp is None:
                    continue
                if isinstance(success_code, int) and resp.status_code == success_code:
                    return resp

                if isinstance(success_code, tuple) or isinstance(success_code, list):
                    if resp.status_code in success_code:
                        return resp
                self.log.warn("请求数据状态码不符合: status_code = {}".format(resp.status_code))
            except Exception as e:
                self.log.error("请求异常: url = {} ".format(url))
                self.log.error(e)
                time.sleep(1)

            # 没有请求到数据则直接切换代理
            self.switch_proxy(proxy_type)

        return None

    def get(self, url, **kwargs):
        """
        get 请求
        :param url:  请求链接
        :param kwargs: 请求参数:
            success_code - 可以返回的状态码
            try_times - 重试次数 默认3次
            proxy_type - 代理类型，参考 ProxyType
            timeout - 请求超时时间
        :return:
        """
        return self.__request(self.__session.get, url, **kwargs)

    def post(self, url, **kwargs):
        """
        post 请求
        :param url:  请求链接
        :param kwargs: 请求参数:
            success_code - 可以返回的状态码 默认200状态码
            try_times - 重试次数 默认3次
            proxy_type - 代理类型，参考 ProxyType
            timeout - 请求超时时间
        :return:
        """
        return self.__request(self.__session.post, url, **kwargs)
